#include <assert.h>
#include <gfx/gl20/programparameter_gl20.h>

#define GL_OFFSET(offset)  ((const GLvoid*)((unsigned long)offset))

ceProgramParameterGL20::ceProgramParameterGL20 (GLint location, ceProgramParameterType type)
	: _location(location)
	, _type(type)
{
	CE_OBJECT_CONSTR
}

ceProgramParameterGL20::~ceProgramParameterGL20()
{
}

void ceProgramParameterGL20::Bind(int x)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform1i(_location, x);
}
void ceProgramParameterGL20::Bind(int x, int y)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform2i(_location, x, y);
}
void ceProgramParameterGL20::Bind(int x, int y, int z)
{
	assert (_location != -1);
	assert (_type == PPT_Uniform);
	glUniform3i(_location, x, y, z);
}
void ceProgramParameterGL20::Bind(int x, int y, int z, int w)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform4i(_location, x, y, z, w);
}

void ceProgramParameterGL20::Bind(float x)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform1f(_location, x);
}
void ceProgramParameterGL20::Bind(float x, float y)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform2f(_location, x, y);
}
void ceProgramParameterGL20::Bind(float x, float y, float z)
{
	assert (_location != -1);
	assert (_type == PPT_Uniform);
	glUniform3f(_location, x, y, z);
}
void ceProgramParameterGL20::Bind(float x, float y, float z, float w)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform4f(_location, x, y, z, w);
}
void ceProgramParameterGL20::Bind(const ceVector2f &v)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform2f(_location, v.x, v.y);
}
void ceProgramParameterGL20::Bind(const ceVector3f &v)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform3f(_location, v.x, v.y, v.z);
}
void ceProgramParameterGL20::Bind(const ceVector4f &v)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform4f(_location, v.x, v.y, v.z, v.w);
}
void ceProgramParameterGL20::Bind(const ceColor4f&c)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform4f(_location, c.r, c.g, c.b, c.a);
}

void ceProgramParameterGL20::Bind(unsigned count, const float* v)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform1fv(_location, count, (float*)v);
}

void ceProgramParameterGL20::Bind(unsigned count, const ceVector2f* v)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform2fv(_location, count, (float*)v);
}

void ceProgramParameterGL20::Bind(unsigned count, const ceVector3f* v)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform3fv(_location, count, (float*)v);
}

void ceProgramParameterGL20::Bind(unsigned count, const ceVector4f* v)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform4fv(_location, count, (float*)v);
}

void ceProgramParameterGL20::Bind(unsigned count, const ceMatrix4f* v)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniformMatrix4fv (_location, count, false, (const GLfloat*)v);
}

void ceProgramParameterGL20::Bind(const ceMatrix4f &m)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniformMatrix4fv(_location, 1, false, (const GLfloat*)(&m._00));
}

namespace
{
GLenum convert_data_type_to_gl_enum (ceDataType type)
{
	switch (type)
		{
		case DT_Float:
			return GL_FLOAT;
		case DT_Double:
			return GL_DOUBLE;
		case DT_Byte:
			return GL_BYTE;
		case DT_UnsignedByte:
			return GL_UNSIGNED_BYTE;
		case DT_Short:
			return GL_SHORT;
		case DT_UnsignedShort:
			return GL_UNSIGNED_SHORT;
		case DT_Int:
			return GL_INT;
		case DT_UnsignedInt:
			return GL_UNSIGNED_INT;
		default:
			assert (false);
		}

	// no real return value but it keeps the compiler calm
	return GL_FLOAT;
}
}

void ceProgramParameterGL20::Bind(int size, int stride, unsigned offset, ceDataType type)
{
	assert (_type == PPT_Attribute);
	assert (_location != -1);
	GLenum t = convert_data_type_to_gl_enum(type);
	glVertexAttribPointer (_location, size, t, false, stride, GL_OFFSET(offset));
}

void ceProgramParameterGL20::Bind(int size, int stride, const void* data, ceDataType type)
{
	assert (_type == PPT_Attribute);
	assert (_location != -1);
	GLenum t = convert_data_type_to_gl_enum(type);
	glVertexAttribPointer (_location, size, t, false, stride, data);
}

void ceProgramParameterGL20::Bind(int stride, const ceVector2f *vptr)
{
	assert (_type == PPT_Attribute);
	assert (_location != -1);
	glVertexAttribPointer (_location, 2, GL_FLOAT, false, stride, (const GLvoid*)vptr);
}

void ceProgramParameterGL20::Bind(int stride, const ceVector3f *vptr)
{
	assert (_type == PPT_Attribute);
	assert (_location != -1);
	glVertexAttribPointer (_location, 3, GL_FLOAT, false, stride, (const GLvoid*)vptr);
}

void ceProgramParameterGL20::Bind(int stride, const ceVector4f *vptr)
{
	assert (_type == PPT_Attribute);
	assert (_location != -1);
	glVertexAttribPointer (_location, 4, GL_FLOAT, false, stride, (const GLvoid*)vptr);
}

void ceProgramParameterGL20::Bind(ceTextureStage state, const iTexture2D* texture)
{
	assert (_type == PPT_Uniform);
	assert (_location != -1);
	glUniform1i(_location, state);
}


void ceProgramParameterGL20::Enable()
{
	assert (_type == PPT_Attribute);
	assert (_location != -1);
	glEnableVertexAttribArray (_location);
}

void ceProgramParameterGL20::Disable()
{
	assert (_type == PPT_Attribute);
	assert (_location != -1);
	glDisableVertexAttribArray (_location);
}
